package com.thouiot.modbus.component;

import com.serotonin.modbus4j.ModbusSlaveSet;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.ip.tcp.TcpSlave;
import com.serotonin.modbus4j.ip.udp.UdpSlave;
import com.serotonin.modbus4j.serial.ascii.AsciiSlave;
import com.serotonin.modbus4j.serial.rtu.RtuSlave;
import com.thouiot.modbus.config.ModbusTypeEnum;
import com.thouiot.modbus.config.properties.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @description modbus slave 工厂
 * @author wangkai
 * @date 2025/04/14 16:19
 */
@Component
public class ModbusSlaveHolder {

    @Resource
    private ModbusSlaveProperties modbusSlaveProperties;

    private Map<String, ModbusSlaveSet> modbusSlaveMap = new ConcurrentHashMap<>();

    public ModbusSlaveSet getModbusSlave(String name, ModbusTypeEnum type) {
        if (modbusSlaveMap.containsKey(name)) {
            return modbusSlaveMap.get(name);
        }
        return modbusSlaveMap.computeIfAbsent(name, key -> {
            if (type == ModbusTypeEnum.TCP) {
                return buildTcpSlave(name);
            } else if (type == ModbusTypeEnum.UDP) {
                return buildUdpSlave(name);
            } else if (type == ModbusTypeEnum.RTU) {
                return buildRtuSlave(name);
            } else if (type == ModbusTypeEnum.ASCII) {
                return buildAsciiSlave(name);
            } else {
                return null;
            }
        });
    }

    private TcpSlave buildTcpSlave(String name) {
        List<ModbusSlaveTcp> tcpSlaves = modbusSlaveProperties.getTcpSlaves();
        if (CollectionUtils.isEmpty(tcpSlaves)) {
            return null;
        }
        Map<String, ModbusSlaveTcp> slaveTcpMap = tcpSlaves.stream().collect(Collectors.toMap(ModbusSlaveTcp::getName, Function.identity(), (v1, v2) -> v1));
        if (!slaveTcpMap.containsKey(name)) {
            return null;
        }
        ModbusSlaveTcp modbusSlaveTcp = slaveTcpMap.get(name);
        TcpSlave tcpSlave = new TcpSlave(modbusSlaveTcp.getPort(), modbusSlaveTcp.isEncapsulated());
        // TODO 参数设置，目前使用默认
//        tcpSlave.setMaxReadBitCount(modbusSlaveTcp.getMaxReadBitCount());
//        tcpSlave.setMaxReadRegisterCount(modbusSlaveTcp.getMaxReadRegisterCount());
//        tcpSlave.setMaxWriteRegisterCount(modbusSlaveTcp.getMaxWriteRegisterCount());
//        tcpSlave.setExceptionHandler(new ModbusSlaveExceptionHandler());

        // tcpslave 会阻塞线程，所以单独开线程启动
        new Thread(() -> {
            try {
                tcpSlave.start();
            } catch (ModbusInitException e) {
                throw new RuntimeException(e);
            }
        }).start();
        return tcpSlave;
    }

    private UdpSlave buildUdpSlave(String name) {
        List<ModbusSlaveUdp> udpSlaves = modbusSlaveProperties.getUdpSlaves();
        if (CollectionUtils.isEmpty(udpSlaves)) {
            return null;
        }
        Map<String, ModbusSlaveUdp> slaveUdpMap = udpSlaves.stream().collect(Collectors.toMap(ModbusSlaveUdp::getName, Function.identity(), (v1, v2) -> v1));
       if (!slaveUdpMap.containsKey(name)) {
           return null;
       }
       ModbusSlaveUdp modbusSlaveUdp = slaveUdpMap.get(name);
       UdpSlave udpSlave = new UdpSlave(modbusSlaveUdp.getPort(), modbusSlaveUdp.isEncapsulated());
       new Thread(() -> {
           try {
               udpSlave.start();
           } catch (ModbusInitException e) {
               throw new RuntimeException(e);
           }
       }).start();
       return udpSlave;
    }

    private AsciiSlave buildAsciiSlave(String name) {
        List<ModbusSlaveAscii> asciiSlaves = modbusSlaveProperties.getAsciiSlaves();
        if (CollectionUtils.isEmpty(asciiSlaves)) {
            return null;
        }
        Map<String, ModbusSlaveAscii> slaveAsciiMap = asciiSlaves.stream().collect(Collectors.toMap(ModbusSlaveAscii::getName, Function.identity(), (v1, v2) -> v1));
        if (!slaveAsciiMap.containsKey(name)) {
            return null;
        }
        ModbusSlaveAscii modbusSlaveAscii = slaveAsciiMap.get(name);
        AsciiSlave asciiSlave = new AsciiSlave(modbusSlaveAscii);
        try {
            asciiSlave.start();
        } catch (ModbusInitException e) {
            throw new RuntimeException(e);
        }
        return asciiSlave;
    }

    private RtuSlave buildRtuSlave(String name) {
        List<ModbusSlaveRtu> rtuSlaves = modbusSlaveProperties.getRtuSlaves();
        if (CollectionUtils.isEmpty(rtuSlaves)) {
            return null;
        }
        Map<String, ModbusSlaveRtu> slaveRtuMap = rtuSlaves.stream().collect(Collectors.toMap(ModbusSlaveRtu::getName, Function.identity(), (v1, v2) -> v1));
        if (!slaveRtuMap.containsKey(name)) {
            return null;
        }
        ModbusSlaveRtu modbusSlaveRtu = slaveRtuMap.get(name);
        RtuSlave rtuSlave = new RtuSlave(modbusSlaveRtu);
        try {
            rtuSlave.start();
        } catch (ModbusInitException e) {
            throw new RuntimeException(e);
        }
        return rtuSlave;
    }

    private void initModbusConfig(ModbusSlaveSet modbusSlaveSet) {
//        modbusSlaveSet.setMaxReadBitCount(modbusSlaveTcp.getMaxReadBitCount());
//        modbusSlaveSet.setMaxReadRegisterCount(modbusSlaveTcp.getMaxReadRegisterCount());
//        modbusSlaveSet.setMaxWriteRegisterCount(modbusSlaveTcp.getMaxWriteRegisterCount());
//        modbusSlaveSet.setExceptionHandler(new ModbusSlaveExceptionHandler());
    }

    public void closeModbusSlaves() {
        modbusSlaveMap.forEach((k, v) -> v.stop());
    }

    public void initModbusSlaves() {
        if (!modbusSlaveProperties.isEnableInit()) {
            return;
        }
        Optional.ofNullable(modbusSlaveProperties.getTcpSlaves()).
                ifPresent(tcpSlaves -> tcpSlaves.forEach(
                        tcpSlave -> getModbusSlave(tcpSlave.getName(), ModbusTypeEnum.TCP)));
        Optional.ofNullable(modbusSlaveProperties.getUdpSlaves())
                .ifPresent(udpSlaves -> udpSlaves.forEach(
                        udpSlave -> getModbusSlave(udpSlave.getName(), ModbusTypeEnum.UDP)));
        Optional.ofNullable(modbusSlaveProperties.getRtuSlaves())
                .ifPresent(rtuSlaves -> rtuSlaves.forEach(
                        rtuSlave -> getModbusSlave(rtuSlave.getName(), ModbusTypeEnum.RTU)));
        Optional.ofNullable(modbusSlaveProperties.getAsciiSlaves())
                .ifPresent(asciiSlaves -> asciiSlaves.forEach(
                        asciiSlave -> getModbusSlave(asciiSlave.getName(), ModbusTypeEnum.ASCII)));
    }
}
